<?php
class My_DataBase_Mapper_Table
{
    /**
     * Name of the table to map
     * @var string
     */
    protected $tableName;

    /**
     * Array of My_DataBase_Mapper_Table 's
     * @var array
     */
    protected $columnsToMap = array();

    /**
     * Name for the mapper class
     * @var My_Code_Class
     */
    protected $mapperClass;

    /**
     * Name for the finder class
     * @var My_Code_Class
     */
    protected $finderClass;

    /**
     * Output directory for the mapper and finder classes
     * @var string
     */
    protected $outputDirectory;

    /**
     * Prefix for class names
     * @var string
     */
    protected $prefixForClassName;

    /**
     * Protected constructor (@see Factory)
     */
    protected function  __construct()
    {
    }

    /**
     * @return My_DataBase_Mapper_Table
     */
    public static function factory()
    {
        return new self();
    }

    /**
     * Set output directory for generated classes
     * @param string $dir
     * @return My_DataBase_Mapper_Table
     *
     */
    public function setOutputDir($dir)
    {
        $this->outputDirectory = $dir;
        return $this;
    }

    
    /**
     * Set prefix for generated classes
     * @param <type> $prefix
     * @return My_DataBase_Mapper_Table 
     */
    public function setPrefixForClassName($prefix)
    {
        $this->prefixForClassName = $prefix;
        return $this;
    }


    /**
     * Set the name of the table to map
     * @param string $tableName
     * @return My_DataBase_Mapper_Table
     */
    public function setTableName($tableName)
    {
        $this->tableName = $tableName;
        return $this;
    }


    /**
     * Add a column to the map
     * @param My_DataBase_Mapper_Column $columnName
     * @return My_DataBase_Mapper_Table
     */
    public function addColumnToMap(My_DataBase_Mapper_Column $column)
    {
        $this->columnsToMap[] = $column;
        return $this;
    }


    /**
     * Do the map of the table
     * @return string
     */
    public function doMap()
    {
        $this->parseTable();

        $this->createMapperClass();
        $code['mapperClass'] = $this->mapperClass->toString();
        //My_Logger::getInstance()->dump($this->mapperClass->toString(), 'mapper');
        //file_put_contents($this->outputDirectory.  ucfirst($this->tableName).'.php', '<?php '.$this->mapperClass->toString());
        
        $this->createFinderClass();
        $code['finderClass'] = $this->finderClass->toString();
        //My_Logger::getInstance()->dump($this->finderClass->toString(), 'finder');
        //file_put_contents($this->outputDirectory . 'Finder'.'.php', '<?php '.$this->finderClass->toString());
        return $code;
    }

    /**
     * Parse table
     */
    protected function parseTable()
    {
        $dataBase = Zend_Db_Table::getDefaultAdapter();
        $tableDescriptionData = $dataBase->describeTable($this->tableName);
        foreach ($tableDescriptionData as $columnData)
        {
            $column = My_DataBase_Mapper_Column::factory()
                ->setName($columnData['COLUMN_NAME'])
                ->setType($columnData['DATA_TYPE'])
                ->setPrimary($columnData['PRIMARY']);
            $this->addColumnToMap($column);
        }
    }

    /**
     * Create php code for mapper class
     */
    protected function createMapperClass()
    {

        $mapperClass = My_Code_Class::factory()->setName($this->prefixForClassName.'_'.ucfirst($this->tableName));
        $mapperClass->factoryPattern(array(My_Code_Function_Parameter::factory()->setName('dataArray')->setType('array')->setValue(null)));

        // loadArray method
        $loadArray = My_Code_Function::factory()
            ->setVisibility('public')->isStatic()
            ->setName('loadArray')
            ->setReturnType($this->prefixForClassName.'_'.ucfirst($this->tableName))
            ->addParameter(My_Code_Function_Parameter::factory()->setName('dataArray')->setType('array'))
            ->createComments();
        $loadArray->addCodeLine(My_Code_Line::factory('return new self($dataArray)'));
        $mapperClass->addMethod($loadArray);

        // Properties, getters, setters...
        foreach ($this->columnsToMap as $column)
        {
            
            if (true === $column->isPrimary())
            {
               // Add loadBy method
               $loadBy = My_Code_Function::factory()
                    ->createComments()
                    ->setVisibility('public')->isStatic()
                    ->setName('loadBy'.ucfirst($column->getName()))
                    ->addParameter(My_Code_Function_Parameter::factory()->setName($column->getName())->setType($column->getType()))
                    ->setReturnType($this->prefixForClassName.'_'.ucfirst($this->tableName));
               $loadBy->addCodeLine(My_Code_Line::factory(sprintf('$result = %s_Finder::factory()->setReturnType(\'%s\')->%s($%s)->limit(1)->find()',
                   $this->prefixForClassName.'_'.ucfirst($this->tableName),
                   $this->prefixForClassName.'_'.ucfirst($this->tableName),
                   $column->getName(),
                   $column->getName())));
               $loadBy->addCodeLine(My_Code_Line::factory('return $result->current()'));
               $mapperClass->addMethod($loadBy);

               // Add save method
               $save = My_Code_Function::factory()
                   ->createComments()
                   ->setVisibility('public')
                   ->setName('save')
                   ->setReturnType($this->prefixForClassName.'_'.ucfirst($this->tableName));
               $save->addCodeLine(My_Code_Line::factory('$dataBase = Zend_Db_Table::getDefaultAdapter()'));
               $save->addCodeLine(My_Code_Line::factory(sprintf('$dataBase->insert(\'%s\', $this->toArray())', $this->tableName, $column->getName())));
               $save->addCodeLine(My_Code_Line::factory(sprintf('$this->%s = $dataBase->lastInsertId()', $column->getName())));
               $save->addCodeLine(My_Code_Line::factory('return $this'));
               $mapperClass->addMethod($save);

               // Add update method
               $update = My_Code_Function::factory()
                    ->createComments()
                    ->setVisibility('public')
                    ->setName('update')
                    ->setReturnType($this->prefixForClassName.'_'.ucfirst($this->tableName));
               $update->addCodeLine(My_Code_Line::factory('$dataBase = Zend_Db_Table::getDefaultAdapter()'));
               $update->addCodeLine(My_Code_Line::factory(sprintf('$where[\'%s = ?\'] = $this->get%s()', $column->getName(), ucfirst($column->getName()) )));
               $update->addCodeLine(My_Code_Line::factory(sprintf('$dataBase->update(\'%s\', $this->toArray(), $where)', $this->tableName)));
               $update->addCodeLine(My_Code_Line::factory('return $this'));
               $mapperClass->addMethod($update);
            }
            
            $classProperty = My_Code_Class_Property::factory()
                ->setName($column->getName())
                ->setType($column->getType())
                ->setVisibility('protected');
            $mapperClass->addProperty($classProperty, true, true, true);


        }

        // Add toArray method
        $properties = $mapperClass->getProperties();
        $toArray = My_Code_Function::factory()
            ->setName('toArray')
            ->setVisibility('public')
            ->setReturnType('array')
            ->createComments();
        $toArray->addCodeLine(My_Code_Line::factory('$data = array()'));
        foreach ($properties as $property)
        {
            $toArray->addCodeLine(My_Code_Line::factory(sprintf('$data[\'%s\'] = $this->%s', $property->getName(), $property->getName() )));
        }
        $toArray->addCodeLine(My_Code_Line::factory('return $data'));
        $mapperClass->addMethod($toArray);

        $this->mapperClass = $mapperClass;
    }


    /**
     * Create finder class
     */
    protected function createFinderClass()
    {
        $className = ucfirst($this->prefixForClassName.'_'.ucfirst($this->tableName)).'_Finder';
        $finderClass = My_Code_Class::factory()->setName($className)
            ->factoryPattern()->setParent('My_DataBase_Finder');

        $finderClass->addProperty(My_Code_Class_Property::factory()
            ->setName('tableName')
            ->setVisibility('protected')
            ->setType('string')
            ->setDefaultValue($this->tableName));

        // Conditions 
        foreach ($this->columnsToMap as $col)
        {
            $addCondition = My_Code_Function::factory()
                ->setVisibility('public')
                ->setName($col->getName())
                ->setReturnType($className)->createComments()
                ->addParameter(My_Code_Function_Parameter::factory()->setName($col->getName())->setType($col->getType()));
            $addCondition->addCodeLine(My_Code_Line::factory(sprintf('$this->addCondition(\'%s\', $%s)', $col->getName(), $col->getName())));
            $addCondition->addCodeLine(My_Code_Line::factory('return $this'));
            $finderClass->addMethod($addCondition);
        }
        $this->finderClass = $finderClass;
    }

}